/*******************************************************************************
 * Witz - Winboard Chess Engine                                                *
 * CONFIG.C                                                                    *
 *                                                                             *
 * Reads from a text file called config.ini and configures Witz with many      *
 * Parameters.                                                                 *
 *                                                                             *
 * Please refer to the licence notice in MAIN.C                                *
 *                                                                             *
 * Copywright (C) 2006 Pradu Kannan                                            *
 * http://witz.sf.net/                                                         *
 ******************************************************************************/

#include <windows.h>

#include "build.h"
#include "defs.h"
#include "protos.h"
#include "data.h"

#include "threads.h"

/************************************
 *TT_SIZE
 *Transposition Table size
 *This number must be a prime number for the best performance
 *http://witz.sf.net/Other/Primes.jar*/
U32 TT_SIZE;

//an array telling how many TT entries are needed to take up
//2^n MB of space - all prime numbers
const int TT_SIZES[] = {43669,87359,174761,349519,699037,1398091,
                       2796181,5592373,11184799,22369601,44739181,89478457};

//config file pointer (private variable)
FILE* config;

//path to the configuration file
char configPath[512];

//Book Related

//location of the book
bool hasBook;        //should the program use the book or not
char bookPath[2048]; //path to the book
int gamesWeight;     //weight givien for number of games in book
int winsWeight;      //weight given for % wins in book

//what kind of search to use
int searchType;
bool checkEvasions, recaptureExtensions;

//ICS_PV when to print ICS output
double ICS_PV;

/*readConfig()
 *tries to read the config file
 *if the file is not found, it creates a new one
 *it uses default values for values that cannot be read
 *
 *NOTE: I found a bug in VC2003 Compiler.
 *It cannot do this properly:
 *
 *char a,b[256]="something = 1\n";
 *sscanf(b, "%*s %*s %d", &a);
 *
 *I had to use junk integer to read this in properly in the following code
 */
void readConfig()
{
	//here are booleans to test whether the specified value was declared in the
	bool hash=false;
	bool PVAL=false,NVAL=false,BVAL=false,RVAL=false,QVAL=false,KVAL=false;
	bool Mob=false, Cap=false, KS=false;
	bool ST=false, CE=false, RECAP=false;
	bool HASBOOK=false,BOOKPATH=false,BOOKPATHRELATIVE=false,GAMESWEIGHT=false,WINSWEIGHT=false;
	bool ICSPV=false;
	bool PASSEDPAWNPST=false;
	bool HALFPASSEDPAWNPST=false;
	bool DOUBLEDPAWNPST=false;
	bool ISOLATEDPAWNPST=false;
	bool NOTDEFENDABLEPAWNPST=false;
	bool KSSCALESTART=false, KSSCALEEND=false;
	char line[2048], parameter[2048];
	int junkInteger;

	//try to open config.ini for reading
	config=fopen(configPath,"rt");
	if(config==NULL) //if config file dosen't exist
	{
		printf("%s not found!\n",configPath);
		printf("trying to create a new %s...\n",configPath);
		config=fopen(configPath,"wt");
		if(config==NULL)
			printf("unable to create %s!\n",configPath);
		else
		{
			printf("config.ini created successfully!\n");
			writeDefaultValues();
			if(ferror(config))
				printf("could not write to the new %s file!\n",configPath);
			else
				printf("wrote %s with default values\n",configPath);
			fclose(config);
		}
	}
	else //read values from config file
	{
		while(fgets(line, 256, config))
		{
			//ignore newlines and comments
			if(*line=='\n' || *line=='/') continue;
			//get the parameter name
			sscanf(line, "%s", parameter);
			stringToUpperCase(parameter);
			if(!strcmp("HASH",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &TT_SIZE))
					continue;
				if(TT_SIZE>12)
				{
					printf("Invalid HASH value\n");
					continue;
				}
				hash=true;
				TT_SIZE=TT_SIZES[TT_SIZE];
				continue;
			}
			if(!strcmp("PVAL",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &Pval))
					continue;
				PVAL=true;
				continue;
			}
			if(!strcmp("NVAL",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &Nval))
					continue;
				NVAL=true;
				continue;
			}
			if(!strcmp("BVAL",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &Bval))
					continue;
				BVAL=true;
				continue;
			}
			if(!strcmp("RVAL",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &Rval))
					continue;
				RVAL=true;
				continue;
			}
			if(!strcmp("QVAL",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &Qval))
					continue;
				QVAL=true;
				continue;
			}
			if(!strcmp("KVAL",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &Kval))
					continue;
				KVAL=true;
				continue;
			}
			if(!strcmp("MOBILITY",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &MOB_WEIGHT))
					continue;
				Mob=true;
				continue;
			}
			if(!strcmp("CAPTURES",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &CAPTURE_BONUS))
					continue;
				Cap=true;
				continue;
			}
			if(!strcmp("KINGSAFETY",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &KS_WEIGHT))
					continue;
				KS=true;
				continue;
			}
			if(!strcmp("PASSEDPAWNPST",parameter))
			{
				if(read64IntArray(passedPawnPST))
					PASSEDPAWNPST=true;
				continue;
			}
			if(!strcmp("HALFPASSEDPAWNPST",parameter))
			{
				if(read64IntArray(halfpassedPawnPST))
					HALFPASSEDPAWNPST=true;
				continue;
			}
			if(!strcmp("DOUBLEDPAWNPST",parameter))
			{
				if(read64IntArray(doubledPawnPST))
					DOUBLEDPAWNPST=true;
				continue;
			}
			if(!strcmp("ISOLATEDPAWNPST",parameter))
			{
				if(read64IntArray(isolatedPawnPST))
					ISOLATEDPAWNPST=true;
				continue;
			}
			if(!strcmp("NOTDEFENDABLEPAWNPST",parameter))
			{
				if(read64IntArray(notDefendablePawnPST))
					NOTDEFENDABLEPAWNPST=true;
				continue;
			}
			if(!strcmp("KS_SCALESTART",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &KS_ScaleStart))
					continue;
				KSSCALESTART=true;
				continue;
			}
			if(!strcmp("KS_SCALEEND",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &KS_ScaleEnd))
					continue;
				KSSCALEEND=true;
				continue;
			}
			if(!strcmp("SEARCHTYPE",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &searchType))
					continue;
				ST=true;
				continue;
			}
			if(!strcmp("CHECKEVASIONS",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &junkInteger))
					continue;
				checkEvasions=junkInteger;
				CE=true;
				continue;
			}
			if(!strcmp("RECAPTUREEXTENSIONS",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &junkInteger))
					continue;
				recaptureExtensions=junkInteger;
				RECAP=true;
				continue;
			}
			if(!strcmp("USEBOOK",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &junkInteger))
					continue;
				hasBook=junkInteger;
				HASBOOK=true;
				continue;
			}
			if(!strcmp("BOOKPATHRELATIVE",parameter))
			{
				BOOKPATHRELATIVE=true;
				continue;
			}
			if(!strcmp("BOOKPATH",parameter))
			{
				if(!sscanf(line, "%*s %*s %s", bookPath))
					continue;
				BOOKPATH=true;
				continue;
			}
			if(!strcmp("GAMESWEIGHT",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &gamesWeight))
					continue;
				GAMESWEIGHT=true;
				continue;
			}
			if(!strcmp("WINSWEIGHT",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &winsWeight))
					continue;
				WINSWEIGHT=true;
				continue;
			}
			if(!strcmp("ICSPV",parameter))
			{
				if(!sscanf(line, "%*s %*s %d", &junkInteger))
					continue;
				ICS_PV=junkInteger/((double)100);
				ICSPV=true;
				continue;
			}
			printf("Unrecognized Parameter %s\n",parameter);
		}
		fclose(config);
	}

	if(!hash)
	{
		printf("Hash size not set - using %d\n",default_TT_SIZE);
		TT_SIZE=TT_SIZES[default_TT_SIZE];
	}
	TT=malloc(sizeof(TTentry)*TT_SIZE);

	InitQueue(&operationQueue, 100);
	InitQueue(&resultQueue, 100);
	InitThreads(NO_PROCESSORS, &operationQueue, &resultQueue);
	InitializeCriticalSection(&TTLock);

	if(!PVAL)
	{
		printf("Pawn value not set - using %d\n",default_Pval);
		Pval=default_Pval;
	}
	if(!NVAL)
	{
		printf("Knight value not set - using %d\n",default_Nval);
		Nval=default_Nval;
	}
	if(!BVAL)
	{
		printf("Bishop value not set - using %d\n",default_Bval);
		Bval=default_Bval;
	}
	if(!RVAL)
	{
		printf("Rook value not set - using %d\n",default_Rval);
		Rval=default_Rval;
	}
	if(!QVAL)
	{
		printf("Queen value not set - using %d\n",default_Qval);
		Qval=default_Qval;
	}
	if(!KVAL)
	{
		printf("King value not set - using %d\n",default_Kval);
		Kval=default_Kval;
	}
	PieceVal[0]=EMPTYval;
	PieceVal[1]=Pval;
	PieceVal[2]=Nval;
	PieceVal[3]=Bval;
	PieceVal[4]=Rval;
	PieceVal[5]=Qval;
	PieceVal[6]=Kval;
	if(!Mob)
	{
		printf("Mobility value not set - using %d\n",default_MOB_WEIGHT);
		MOB_WEIGHT=default_MOB_WEIGHT;
	}
	if(!Cap)
	{
		printf("Capture value not set - using %d\n",default_CAPTURE_BONUS);
		CAPTURE_BONUS=default_CAPTURE_BONUS;
	}
	if(!KS)
	{
		printf("King Safety value not set - using %d\n",default_KS_WEIGHT);
		KS_WEIGHT=default_KS_WEIGHT;
	}
	if(!PASSEDPAWNPST)
	{
		printf("Passed Pawn Piece Square Table not set - using default\n");
		copyArray(passedPawnPST,default_passedPawnPST,64);
	}
	if(!HALFPASSEDPAWNPST)
	{
		printf("Half-Passed Pawn Piece Square Table not set - using default\n");
		copyArray(halfpassedPawnPST,default_halfpassedPawnPST,64);
	}
	if(!DOUBLEDPAWNPST)
	{
		printf("Doubled Pawn Piece Square Table not set - using default\n");
		copyArray(doubledPawnPST,default_doubledPawnPST,64);
	}
	if(!ISOLATEDPAWNPST)
	{
		printf("Isolated Pawn Piece Square Table not set - using default\n");
		copyArray(isolatedPawnPST,default_isolatedPawnPST,64);
	}
	if(!NOTDEFENDABLEPAWNPST)
	{
		printf("Not-Defendable Pawn Piece Square Table not set - using default\n");
		copyArray(notDefendablePawnPST,default_notDefendablePawnPST,64);
	}

	if(!KSSCALESTART)
	{
		printf("KS_ScaleStart not defined, using %d\n", default_KS_ScaleStart);
		KS_ScaleStart=default_KS_ScaleStart;
	}
	if(!KSSCALEEND)
	{
		printf("KS_ScaleEnd not defined, using %d\n", default_KS_ScaleEnd);
		KS_ScaleStart=default_KS_ScaleEnd;
	}

	if(KS_ScaleEnd>=KS_ScaleStart)
	{
		printf("Invalid KS_ScaleStart and KS_ScaleEnd\n");
		printf("For KS_ScaleStart, using %d\n", default_KS_ScaleStart);
		printf("For KS_ScaleEnd, using %d\n", default_KS_ScaleEnd);
		KS_ScaleStart=default_KS_ScaleStart;
		KS_ScaleStart=default_KS_ScaleEnd;
	}

	KS_SUM=(float)(KS_ScaleStart-KS_ScaleEnd);
	
	if(!ST)
	{
		printf("Search type not set - using %d\n",default_searchType);
		searchType=default_searchType;
	}
	if(!CE)
	{
		printf("Check Evasions not set - using %d\n",default_checkEvasions);
		checkEvasions=default_checkEvasions;
	}
	if(!RECAP)
	{
		printf("Recapture Extensions not set - using %d\n",default_recaptureExtensions);
		recaptureExtensions=default_recaptureExtensions;
	}
	if(!HASBOOK)
	{
		printf("UseBook not set - using %d\n", default_hasBook);
		hasBook=default_hasBook;
	}
	if(!BOOKPATH && hasBook)
	{
		printf("BookPath not set - using %s\n", default_bookPath);
		strcpy(bookPath,default_bookPath);
	}
	if(!GAMESWEIGHT && hasBook)
	{
		printf("GamesWeight not set - using %d\n", default_gamesWeight);
		gamesWeight=default_gamesWeight;
	}
	if(!WINSWEIGHT && hasBook)
	{
		printf("WinsWeight not set - using %d\n", default_winsWeight);
		winsWeight=default_winsWeight;
	}

	//don't use book if loading a dump file
	if(dumpFileLocationPointer!=NULL)
		hasBook=false;

	if(hasBook) //make sure the book file is valid
	{
		char bookPathTemp[1024];
		strcpy(bookPathTemp,bookPath);
		if(BOOKPATHRELATIVE)
			sprintf(bookPath,"%s%s",currentDir,bookPathTemp);
		openBook();
		//if open book determined that the file dosen't exist or isn't valid
		if(!hasBook) printf("Could not open %s\n",bookPath);
		closeBook();
	}

	if(!ICSPV)
	{
		printf("ICS PV TIME not set - using %d\n", (int)(default_ICS_PV*100));
		ICS_PV=default_ICS_PV;
	}
}

/*writeDefaultValues
 *writes default values into the config file
 *
 *@prereq FILE *config; must be initalized for writing
 */
void writeDefaultValues()
{
	fprintf(config,"//Witz %s Configuration File\n",PROGRAM_VERSION);
	fprintf(config,"//See Readme file for more information\n\n",PROGRAM_VERSION);

	fprintf(config,"//Hash Table Size\n");
	fprintf(config,"//0=1MB, 1=2MB, 2=4MB, 3=8MB, 4=16MB, 5=32MB, 6=64MB,\n");
	fprintf(config,"//7=128MB, 8=256MB, 9=512MB, 10=1024MB, 11=2048MB\n");
	fprintf(config,"Hash = %d\n\n",default_TT_SIZE);

	fprintf(config,"//Evaluvation Values\n\n");

	fprintf(config,"//Piece Values\n");
	fprintf(config,"Pval = %d\n",default_Pval);
	fprintf(config,"Nval = %d\n",default_Nval);
	fprintf(config,"Bval = %d\n",default_Bval);
	fprintf(config,"Rval = %d\n",default_Rval);
	fprintf(config,"Qval = %d\n",default_Qval);
	fprintf(config,"Kval = %d\n\n",default_Kval);

	fprintf(config,"//Mobility values\n");
	fprintf(config,"Mobility = %d\n",default_MOB_WEIGHT);
	fprintf(config,"Captures = %d\n",default_CAPTURE_BONUS);
	fprintf(config,"KingSafety = %d\n\n",default_KS_WEIGHT);

	fprintf(config,"//Pawn Structure\n");
	fprintf(config,"PassedPawnPST\n");
	write64IntArray(default_passedPawnPST);
	fprintf(config,"HalfPassedPawnPST\n");
	write64IntArray(default_halfpassedPawnPST);
	fprintf(config,"DoubledPawnPST\n");
	write64IntArray(default_doubledPawnPST);
	fprintf(config,"IsolatedPawnPST\n");
	write64IntArray(default_isolatedPawnPST);
	fprintf(config,"NotDefendablePawnPST\n");
	write64IntArray(default_notDefendablePawnPST);

	fprintf(config,"//Scalers\n");
	fprintf(config,"KS_ScaleStart = %d\n", default_KS_ScaleStart);
	fprintf(config,"KS_ScaleEnd = %d\n\n", default_KS_ScaleEnd);

	fprintf(config,"//Search\n");
	fprintf(config,"SearchType = %d\n",default_searchType);
	fprintf(config,"CheckEvasions = %d\n",default_checkEvasions);
	fprintf(config,"RecaptureExtensions = %d\n\n",default_recaptureExtensions);

	fprintf(config,"//Book Options\n");
	fprintf(config,"UseBook = %d\n", default_hasBook);
	fprintf(config,"BookPathRelative\n");
	fprintf(config,"BookPath = %s\n", default_bookPath);
	fprintf(config,"GamesWeight = %d\n", default_gamesWeight);
	fprintf(config,"WinsWeight = %d\n\n", default_winsWeight);
	
	fprintf(config,"//Other\n\n");

	fprintf(config,"//%%time left from alloted time to start printing to ICS\n");
	fprintf(config,"ICSPV = %d\n",(int)(default_ICS_PV*100));
}

/*stringToUpperCase()
 *converts a string to upper case
 *
 *@param string[256] - the string to convert to upper case
 */
void stringToUpperCase(char *string)
{
	int i=0;
	while(string[i]!='\0')
	{
		string[i]=toupper(string[i]);
		i++;
	}
}

/*read64IntArray()
 *reads an array of 64 integers arranged in a chessboard fashion
 *
 *@param arr - the array to read into
 *
 *@returns true if the operation was successful
 */
bool read64IntArray(int arr[64])
{
	int i;
	for(i=56;i>=0;i-=8)
	{
		char line[256];
		if(!fgets(line, 256, config)) return false;
		if(sscanf(line,"%d %d %d %d %d %d %d %d",arr+i,arr+i+1,arr+i+2,
			arr+i+3,arr+i+4,arr+i+5,arr+i+6,arr+i+7)!=8)
			return false;
	}
	return true;
}

/*write64IntArray()
 *writes an array of 64 integers arranged in a chessboard fashion
 *to the config file
 *
 *@param arr - the array to write
 */
void write64IntArray(int arr[64])
{
	int i,j,width=1;
	//get width of the largest element in the array
	for(i=0;i<64;i++)
	{
		int tempWidth=0;
		j=arr[i];
		do
		{
			j/=10;
			tempWidth++;
		}while(j);
		if(arr[i]<0) tempWidth++;
		if(tempWidth>width) width=tempWidth;
	}

	printf("%d\n",width);

	for(i=56;i>=0;i-=8)
	{
		for(j=0;j<8;j++)
		{
			fprintf(config,"%*d",width,arr[i+j]);
			if(j!=7)
				fprintf(config," ");
		}
		fprintf(config,"\n");
	}
	fprintf(config,"\n");
}

/*copyArray()
 *copies values from one integer array to another
 *
 *@param arrCopy - the array to be copied to
 *@param arrOriginal - the array that is being copied from
 *
 */
void copyArray(int* arrCopy, int* arrOriginal, int size)
{
	memcpy(arrCopy,arrOriginal,sizeof(int)*size);
}